home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Best of MacTutor - S…e Code for Volumes 1 to 5
/
The Best of MacTutor - Source Code for Volume 1-5 (Wayzata Technology)(6031)(1990).bin
/
Source Code
/
#50 (Nov 89)
/
MIDIArp.Application
/
MIDIArp.Application⁄NO LICENSE
/
MIDIArp.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-06-29
|
29KB
|
1,152 lines
/*
File: MIDIArp.c
Usage: MIDI Manager Arpeggiator Program.
Purpose: Apple MIDI Manager Demo.
Authors: Fred Malouf,
Don Marsh,
Don Veca.
Date: April 1989
*/
#include <stdIO.h>
#include <stdlib.h>
#include <Values.h>
#include <Types.h>
#include <Resources.h>
#include <QuickDraw.h>
#include <Fonts.h>
#include <Events.h>
#include <Controls.h>
#include <Windows.h>
#include <Menus.h>
#include <TextEdit.h>
#include <Dialogs.h>
#include <Desk.h>
#include <Scrap.h>
#include <ToolUtils.h>
#include <Memory.h>
#include <SegLoad.h>
#include <Files.h>
#include <OSUtils.h>
#include <OSEvents.h>
#include <Traps.h>
#include <Errors.h>
#include <Sound.h>
#include <MIDI.h>
#include "MIDIDefs.h"
#include "MIDIArp.h"
#define updatePeriod 5 // For WaitNextEvent().
DialogPtr GMainDialog; // The Main Dialog Box Pointer.
ArpParams ArpGlobals; // Global data for the arpeggiator.
short GCurSpeedID; // Currently selected speed control.
Boolean GManualPatch; // True if not launched by a PatchBay Config. File.
Boolean GDone = false; // Global Exit Flag.
char GMIDIMgrVerStr[256]; // MIDI Manager Ver (Std Mac Ver # String)
main()
{
InitThings(); // Initialize Managers and some App globals.
ArpInit(); // Sign in to MIDI Mgr and add time/data ports.
StartDialog(); // Bring up main dialog box with default values.
RunDialog(); // Handle events.
ArpClose(); // Sign out from MIDI Mgr.
StopDialog(); // Close dialog box.
}
// Initialize Managers and other generic stuff.
void
InitThings(void)
{
FlushEvents(everyEvent, 0); // Empty any stray events.
InitGraf(&qd.thePort); // Initialize quickdraw.
InitFonts(); // Initialize Font Manager.
InitWindows(); // Initialize Window Manager.
InitMenus(); // Initialize Menu Manager.
TEInit(); // Initialize Text Edit.
InitDialogs(NULL); // Initialize the Dialog Manager.
InitCursor(); // Turn cursor back to arrow.
{ // Set up a standard menu bar.
Handle MenuBar = GetNewMBar(menuBar); // Read menus into menu bar.
SetMenuBar(MenuBar); // Install menus.
DisposHandle(MenuBar);
AddResMenu( GetMHandle(appleMenu), 'DRVR'); // Add DA names to Apple menu.
DrawMenuBar();
}
// Seed the random number generator.
Seed();
}
// Sign into the MIDI Manager.
// Set up time, input, and output ports.
// Initialize necessary arp globals.
// Start our time base clock.
void
ArpInit(void)
{
MIDIPortParams Init; // MIDI Mgr Init data structure
Handle TheIconHndl;
OSErr TheErr;
long MIDIMgrVerNum; // MIDI Manager Ver (Std Mac Ver #)
char CStrBuf1[256];
// Make sure MIDIMgr is instaled and save version num.
MIDIMgrVerNum = SndDispVersion(midiToolNum);
if (MIDIMgrVerNum == 0)
{
ArpAlert("The MIDI Manager is not installed! Aborting...");
ExitToShell();
}
else
{
StdMacVerNumToStr(MIDIMgrVerNum, GMIDIMgrVerStr);
sprintf(CStrBuf1,"MIDI Manager Version %s", GMIDIMgrVerStr);
ArpAlert(CStrBuf1);
}
// Sign in to the MIDI Manager.
TheIconHndl = GetResource('ICN#', arpIcon);
TheErr = MIDISignIn(arpClientID,
refCon0,
TheIconHndl,
"\pMIDIArp");
if (TheErr)
{
ArpAlert("Trouble signing MIDIArp into MIDI Manager! Aborting...");
ExitToShell();
}
// Assume not a Patchbay configuration.
GManualPatch = true;
// Add time port.
Init.portID = timePortID;
Init.portType = midiPortTypeTime;
Init.timeBase = noTimeBaseRefNum;
Init.readHook = noReadHook;
Init.initClock.sync = midiInternalSync;
Init.initClock.curTime = zeroTime;
Init.initClock.format = midiFormatMSec;
Init.refCon = SetCurrentA5();
C2PStrCpy("TimeBase", Init.name);
TheErr = MIDIAddPort(arpClientID, timePortBuffSize, &(ArpGlobals.TimeRefNum), &Init);
// Has a PatchBay connection been resolved?
if (TheErr == midiVConnectMade)
{
GManualPatch = false;
}
else if (TheErr == memFullErr)
{
ArpAlert("Not enough room in heap zone to add time port! Aborting...");
MIDISignOut(arpClientID);
ExitToShell();
}
// Add an input port.
Init.portID = inputPortID;
Init.portType = midiPortTypeInput;
Init.timeBase = ArpGlobals.TimeRefNum;
Init.offsetTime = midiGetCurrent;
Init.readHook = (Ptr) ArpReader;
Init.refCon = SetCurrentA5();
C2PStrCpy("InputPort", Init.name);
TheErr = MIDIAddPort(arpClientID, inputPortBuffSize, &(ArpGlobals.InputRefNum), &Init);
// Has a PatchBay connection been resolved?
if (TheErr == midiVConnectMade)
{
GManualPatch = false;
}
else if (TheErr == memFullErr)
{
ArpAlert("Not enough room in heap zone to add input port! Aborting...");
MIDISignOut(arpClientID);
ExitToShell();
}
// Add an output port.
Init.portID = outputPortID;
Init.portType = midiPortTypeOutput;
Init.timeBase = ArpGlobals.TimeRefNum;
Init.offsetTime = midiGetCurrent;
Init.readHook = NULL;
Init.refCon = &ArpGlobals;
C2PStrCpy("OutputPort", Init.name);
TheErr = MIDIAddPort(arpClientID, outputPortBuffSize, &(ArpGlobals.OutputRefNum), &Init);
// Has a PatchBay connection been resolved?
if (TheErr == midiVConnectMade)
{
GManualPatch = false;
}
else if (TheErr == memFullErr)
{
ArpAlert("Not enough room in heap zone to add output port! Aborting...");
MIDISignOut(arpClientID);
ExitToShell(); }
// Not a PatchBay patch?
if (GManualPatch)
{
// Connect ports as they were when we last quit.
PatchPorts();
}
// Init other globals.
ArpGlobals.Locked = false;
ArpGlobals.NumNotes = 0;
ArpGlobals.ArpPattern = patternUpDownID;
ArpGlobals.Tempo = speedMedium;
GCurSpeedID = speedMediumID;
//ArpGlobals.ArpTimeProcPtr = ArpTimeProc;
// Start our Clock.
MIDIStartTime(ArpGlobals.TimeRefNum);
}
// Display the dialog. Initialize the radio buttons and static text.
void
StartDialog(void)
{
GMainDialog = GetNewDialog(mainDialogID, NULL, (WindowPtr) -1);
SetPort(GMainDialog);
// Set the radio control buttons to match the initialization
// done in ArpInit().
ChangeState(GMainDialog, ArpGlobals.ArpPattern == patternUpID, patternUpID);
ChangeState(GMainDialog, ArpGlobals.ArpPattern == patternDownID, patternDownID);
ChangeState(GMainDialog, ArpGlobals.ArpPattern == patternUpDownID, patternUpDownID);
ChangeState(GMainDialog, ArpGlobals.ArpPattern == patternDownUpID, patternDownUpID);
ChangeState(GMainDialog, ArpGlobals.ArpPattern == patternRandomID, patternRandomID);
ChangeState(GMainDialog, GCurSpeedID == speedVeryFastID, speedVeryFastID);
ChangeState(GMainDialog, GCurSpeedID == speedFastID, speedFastID);
ChangeState(GMainDialog, GCurSpeedID == speedMediumID, speedMediumID);
ChangeState(GMainDialog, GCurSpeedID == speedSlowID, speedSlowID);
ChangeState(GMainDialog, GCurSpeedID == speedVerySlowID, speedVerySlowID);
ShowWindow(GMainDialog);
StdAdjustDLOGLocation(GMainDialog);
}
// Main Event Loop
void
RunDialog(void)
{
OSErr TheErr = noErr;
short ItemHit;
EventRecord AnEvent;
WindowPtr WhichWindow;
Rect Boundry;
MIDIPortInfoHdl PortInfoH;
GrafPtr SavePort;
while (!GDone) {
// Check to see if we have been connected
// to an external time base.
if ( MIDIWorldChanged(arpClientID) )
{
PortInfoH = MIDIGetPortInfo(arpClientID, timePortID);
if ( (**PortInfoH).timeBase.clientID != noClient )
{
MIDISetSync(ArpGlobals.TimeRefNum, midiExternalSync);
}
else
{
MIDISetSync(ArpGlobals.TimeRefNum, midiInternalSync);
}
DisposHandle((Handle) PortInfoH);
}
if ( WaitNextEvent(everyEvent, &AnEvent, updatePeriod, NULL) )
{
if ( (AnEvent.what == keyDown) && (AnEvent.modifiers & cmdKey) )
{
AdjustMenus();
DoMenuCommand( MenuKey(AnEvent.message & charCodeMask) );
}
if ( IsDialogEvent(&AnEvent) )
{
if (AnEvent.what == updateEvt)
{
GetPort(&SavePort);
SetPort((GrafPtr) AnEvent.message);
StdHiliteButton(GMainDialog, quitID); // Hilite Quit Button.
// TEIdle( ((DialogPeek)GMainDialog)->textH ); // Blink Cursor.
SetPort(SavePort);
}
if (AnEvent.what == keyDown
&& ( (AnEvent.message & charCodeMask) == charEnterKey ))
{
GDone = true;
}
else if ( DialogSelect(&AnEvent, &GMainDialog, &ItemHit) )
{
switch (ItemHit)
{
case quitID:
GDone = true;
break;
case patternUpID:
case patternDownID:
case patternUpDownID:
case patternDownUpID:
case patternRandomID:
SwitchRadio(GMainDialog, &(ArpGlobals.ArpPattern), ItemHit);
break;
case speedVeryFastID:
ArpGlobals.Tempo = speedVeryFast;
SwitchRadio(GMainDialog, &GCurSpeedID, ItemHit);
break;
case speedFastID:
ArpGlobals.Tempo = speedFast;
SwitchRadio(GMainDialog, &GCurSpeedID, ItemHit);
break;
case speedMediumID:
ArpGlobals.Tempo = speedMedium;
SwitchRadio(GMainDialog, &GCurSpeedID, ItemHit);
break;
case speedSlowID:
ArpGlobals.Tempo = speedSlow;
SwitchRadio(GMainDialog, &GCurSpeedID, ItemHit);
break;
case speedVerySlowID:
ArpGlobals.Tempo = speedVerySlow;
SwitchRadio(GMainDialog, &GCurSpeedID, ItemHit);
break;
default:
SysBeep(2);
break;
} // End switch()
} // End DialogSelect()
} // End IsDialogEvent()
else {
switch (AnEvent.what)
{
case mouseDown:
switch( FindWindow(AnEvent.where, &WhichWindow) )
{
case inMenuBar:
AdjustMenus();
DoMenuCommand( MenuSelect(AnEvent.where) );
break;
case inSysWindow:
SystemClick(&AnEvent, WhichWindow);
break;
case inContent:
if ( WhichWindow != FrontWindow() )
{
SelectWindow(WhichWindow);
AdjustMenus();
}
break;
case inGoAway:
if ( TrackGoAway(WhichWindow, AnEvent.where) )
{
GDone = true;
}
break;
case inDrag:
SetRect(&Boundry, 4, 24,
qd.screenBits.bounds.right - 4,
qd.screenBits.bounds.bottom - 4
);
DragWindow(WhichWindow, AnEvent.where, &Boundry);
break;
default:
break;
}
break;
default:
break;
} // End Switch
}
} // End IF WaitNextEvent()
}
}
// Save our patch config if necessary.
// Sign out from the MIDI Manager.
void
ArpClose(void)
{
// Not a PatchBay patch?
if (GManualPatch)
{
// Save current port connection configuration.
SavePatch(timePortID, timePortResInfoID, "timePortInfo");
SavePatch(inputPortID, inputPortResInfoID, "inputPortInfo");
SavePatch(outputPortID, outputPortResInfoID, "outputPortInfo");
}
MIDISignOut(arpClientID);
}
// Shut down the main dialog.
void
StopDialog(void)
{
StdSaveDLOGLocation(GMainDialog, mainDialogID);
HideWindow(GMainDialog);
DisposDialog(GMainDialog);
}
// The Read Hook Function.
// Read all incomming MIDI data.
// Set up first wake up when first
// note of a series is received.
pascal short
ArpReader(MIDIPacket *ThePacketPtr, long TheRefCon)
{
// Set up our A5 world.
long SysA5 = SetA5(TheRefCon);
short RetVal = midiMorePacket, i, j;
if(ArpGlobals.Locked)
{
RetVal = midiKeepPacket;
// Don't want to read packet now,
// I'll get it later.
}
else if ( ThePacketPtr->flags == stdPacketFlags &&
((ThePacketPtr->data[0] & statusMask) == keyOn ||
(ThePacketPtr->data[0] & statusMask) == keyOff)
)
{
RetVal = midiMorePacket;
// Yes, we got this packet thank you,
// please send us more!
/* NOTE ON? */
if ((ThePacketPtr->data[0] & statusMask) == keyOn &&
ThePacketPtr->data[2] != zeroVelo)
{
// Determine where to insert new note.
for (i=0;i<ArpGlobals.NumNotes; i++)
{
if (ThePacketPtr->data[1] <= ArpGlobals.NoteTbl[i].Note)
{
break; // i == index to insert note into.
}
}
// Make room in the note table.
for (j=ArpGlobals.NumNotes; j>i; j--)
{
ArpGlobals.NoteTbl[j].Channel = ArpGlobals.NoteTbl[j-1].Channel;
ArpGlobals.NoteTbl[j].Note = ArpGlobals.NoteTbl[j-1].Note;
ArpGlobals.NoteTbl[j].Velocity = ArpGlobals.NoteTbl[j-1].Velocity;
}
// Insert new note.
ArpGlobals.NoteTbl[i].Channel = ThePacketPtr->data[0] & channelMask;
ArpGlobals.NoteTbl[i].Note = ThePacketPtr->data[1];
ArpGlobals.NoteTbl[i].Velocity = ThePacketPtr->data[2];
// Record number of valid notes in table.
if(ArpGlobals.NumNotes < noteTblSize)
{
ArpGlobals.NumNotes++;
}
if(ArpGlobals.NumNotes == 1) // First note down.
{
// Start off arpeggiation.
ArpGlobals.NextNoteOnTime = ThePacketPtr->tStamp;
ArpTimeProc(ThePacketPtr->tStamp, TheRefCon);
}
}
/* NOTE OFF? */
else if ((ThePacketPtr->data[0] & statusMask) == keyOff ||
(ThePacketPtr->data[0] & statusMask) == keyOn)
// If its keyOn, by now we know its velocity is 0.
{
// Find index in note table that contains same note on same Channel.
for (i=0;i< ArpGlobals.NumNotes; i++)
{
if((ArpGlobals.NoteTbl[i].Channel == (ThePacketPtr->data[0] & channelMask)) &&
(ArpGlobals.NoteTbl[i].Note == ThePacketPtr->data[1]))
{
break; // i == index of note to delete.
}
}
// Still a valid note?
if (i < ArpGlobals.NumNotes)
{
// i is already whrere we want it.
for (/*i=i*/; i<ArpGlobals.NumNotes-1; i++)
{
// Delete (bury) the note.
ArpGlobals.NoteTbl[i].Channel = ArpGlobals.NoteTbl[i+1].Channel;
ArpGlobals.NoteTbl[i].Note = ArpGlobals.NoteTbl[i+1].Note;
ArpGlobals.NoteTbl[i].Velocity = ArpGlobals.NoteTbl[i+1].Velocity;
}
ArpGlobals.NumNotes--;
}
}
}
// Restore the systems A5 world.
SetA5(SysA5);
return(RetVal);
}
// The Time Proc.
// All data output is done from here.
// Sets up next wakeUp based on ArpGlobals,
// or cancels wakeUps if no notes are
// left in the note table.
pascal void
ArpTimeProc(long /* 'TheCurTime' not used here */, long TheRefCon)
{
long SysA5 = SetA5(TheRefCon); // Set up our A5 world.
int i;
MIDIPacket TheMIDIPacket;
// Warn read hook function not to mess with the data structures.
ArpGlobals.Locked = 1;
if (ArpGlobals.NumNotes == 0)
{
// Cancel wakeups until there is a new note to play.
MIDIWakeUp(ArpGlobals.TimeRefNum, zeroTime, zeroPeriod, noTimeProc);
}
else
{
// Set index of next note to be played.
BumpNoteTableIndex();
if (ArpGlobals.NoteTbl[ArpGlobals.NoteIndex] == ArpGlobals.LastNote)
{ // Avoid playing first note
// of an arpeggiation twice.
BumpNoteTableIndex();
}
ArpGlobals.LastNote = ArpGlobals.NoteTbl[ArpGlobals.NoteIndex];
TheMIDIPacket.flags = stdPacketFlags;
TheMIDIPacket.len = keyOnOffPacketSize;
// Turn note on.
i = ArpGlobals.NoteIndex;
TheMIDIPacket.tStamp = ArpGlobals.NextNoteOnTime;
TheMIDIPacket.data[0] = ArpGlobals.NoteTbl[i].Channel | keyOn;
TheMIDIPacket.data[1] = ArpGlobals.NoteTbl[i].Note;
TheMIDIPacket.data[2] = ArpGlobals.NoteTbl[i].Velocity;
MIDIWritePacket(ArpGlobals.OutputRefNum, &TheMIDIPacket);
// Turn note off (in the future).
TheMIDIPacket.tStamp += noteDuration;
TheMIDIPacket.data[0] -= 0x10; // Quick way to convert to a note off.
MIDIWritePacket(ArpGlobals.OutputRefNum, &TheMIDIPacket);
// Set time to play next note .
ArpGlobals.NextNoteOnTime += ArpGlobals.Tempo;
// Schedule next wakeup.
// The MIDIWakeUp will occur early
// (NextNoteOnTime - ArpGlobals.Tempo/2),
// but the time stamp on the note that is being
// written is accurate (NextNoteOnTime).
// This way, we eliminate the chance of the note
// being received late due to processing time.
// The basic rule of thumb is to always write early,
// making sure that the time stamp (which is some
// time in the future) is accurrate.
// The MIDI Manager will take care of the details.
MIDIWakeUp(ArpGlobals.TimeRefNum,
ArpGlobals.NextNoteOnTime - ArpGlobals.Tempo/2,
zeroPeriod,
(ProcPtr) ArpTimeProc
);
}
// OK for ArpReader to mess with the data structures now.
ArpGlobals.Locked = false;
// Restore the systems A5 world.
SetA5(SysA5);
}
// Bumps note index to next note in note table
// based on current user preferences.
void
BumpNoteTableIndex(void)
{
if (ArpGlobals.NumNotes == 1)
{
ArpGlobals.NoteIndex = 0;
}
else
{
switch(ArpGlobals.ArpPattern)
{
case patternUpID:
if (ArpGlobals.NoteIndex >= ArpGlobals.NumNotes-1)
{
ArpGlobals.NoteIndex = 0;
}
else
{
ArpGlobals.NoteIndex++;
}
break;
case patternUpDownID:
case patternDownUpID:
if (ArpGlobals.ArpDirection == goingUp)
{
// First condition necessary
// for the special case of
// one note arpeggiation.
// '>=' for both cases works,
// but top note will be played TWICE.
if (ArpGlobals.NoteIndex > ArpGlobals.NumNotes-1)
{
ArpGlobals.NoteIndex = ArpGlobals.NumNotes-1;
ArpGlobals.ArpDirection = goingDown;
}
else if (ArpGlobals.NoteIndex == ArpGlobals.NumNotes-1)
{
ArpGlobals.NoteIndex--;
ArpGlobals.ArpDirection = goingDown;
}
else
{
ArpGlobals.NoteIndex++;
}
}
else
{
// First condition necessary
// for the special case of
// one note arpeggiation.
// '<=' for both cases works,
// but bottom note will be played TWICE.
if (ArpGlobals.NoteIndex < 0)
{
ArpGlobals.NoteIndex = 0;
ArpGlobals.ArpDirection = goingUp;
}
else if (ArpGlobals.NoteIndex == 0)
{
ArpGlobals.NoteIndex++;
ArpGlobals.ArpDirection = goingUp;
}
else
{
ArpGlobals.NoteIndex--;
}
}
break;
case patternDownID:
if (ArpGlobals.NoteIndex <= 0)
{
ArpGlobals.NoteIndex = ArpGlobals.NumNotes-1;
}
else
{
ArpGlobals.NoteIndex--;
}
break;
case patternRandomID:
ArpGlobals.NoteIndex = Choose(ArpGlobals.NumNotes);
break;
}
}
}
// Get previously saved port connections (port info records)
// from application's 'port' resource.
void
PatchPorts(void)
{
MIDIPortInfoHdl PortInfoH; // Handle to port info record.
MIDIPortInfoPtr PortInfoP; // Pointer to port info record.
short i, TheErr;
// SET UP TIME PORT CONNECTIONS.
PortInfoH = (MIDIPortInfoHdl) GetResource(portResType, timePortResInfoID);
if (PortInfoH == NULL)
{
ReportResError("GetResource(portResType, timePortResInfoID)");
}
HLock((Handle) PortInfoH);
PortInfoP = *PortInfoH;
if (GetHandleSize((Handle) PortInfoH) != 0)
{
// Were we supposed to be sync'd to another client?
if (PortInfoP->timeBase.clientID != noClient)
{
// Yes, so make that client our time base.
TheErr = MIDIConnectTime(
PortInfoP->timeBase.clientID,
PortInfoP->timeBase.portID,
arpClientID,
timePortID
);
// Is the client still signed in?
if (TheErr != midiVConnectErr)
{
// Yes, so set our sync mode to external.
MIDISetSync(ArpGlobals.TimeRefNum, midiExternalSync);
}
}
// Were we somebody else's time base?
for (i=0; i<PortInfoP->numConnects; i++)
{
MIDIConnectTime(arpClientID,
timePortID,
PortInfoP->cList[i].clientID,
PortInfoP->cList[i].portID);
}
}
HUnlock((Handle) PortInfoH);
ReleaseResource((Handle) PortInfoH);
ReportResError("PatchPorts/ReleaseResource()");
// SET UP INPUT PORT CONNECTIONS.
PortInfoH = (MIDIPortInfoHdl) GetResource(portResType, inputPortResInfoID);
if (PortInfoH == NULL)
{
ReportResError("PatchPorts/GetResource()");
}
HLock((Handle) PortInfoH);
PortInfoP = *PortInfoH;
if (GetHandleSize((Handle) PortInfoH) != 0)
{
// Were we connected to anyone?
for (i=0; i<PortInfoP->numConnects; i++)
{
MIDIConnectData(arpClientID,
inputPortID,
PortInfoP->cList[i].clientID,
PortInfoP->cList[i].portID);
}
}
HUnlock((Handle) PortInfoH);
ReleaseResource((Handle) PortInfoH);
ReportResError("PatchPorts/GetResource()");
// SET UP OUTPUT PORT CONNECTIONS.
PortInfoH = (MIDIPortInfoHdl) GetResource(portResType, outputPortResInfoID);
if (PortInfoH == NULL)
{
ReportResError("PatchPorts/GetResource()");
}
HLock((Handle) PortInfoH);
PortInfoP = *PortInfoH;
if (GetHandleSize((Handle) PortInfoH) != 0)
{
// Were we connected to anyone?
for (i=0; i<PortInfoP->numConnects; i++)
{
MIDIConnectData(arpClientID,
outputPortID,
PortInfoP->cList[i].clientID,
PortInfoP->cList[i].portID
);
}
}
HUnlock((Handle) PortInfoH);
ReleaseResource((Handle) PortInfoH);
ReportResError("PatchPorts/ReleaseResource()");
}
// Save current port connections (port info records)
// to application's 'port' resource.
void
SavePatch(OSType PortID, short PortInfoResID, char *PortInfoResName)
{
Handle PortResH; // Handle to ptch resource.
CursHandle WatchCurs;
WatchCurs = GetCursor(watchCursor);
HLock((Handle) WatchCurs);
SetCursor(*WatchCurs);
HUnlock((Handle) WatchCurs);
// Remove existing port info resource.
PortResH = GetResource(portResType, PortInfoResID);
ReportResError("SavePatch/GetResource()");
RmveResource(PortResH);
ReportResError("SavePatch/RmveResource() (make sure disk is unlocked)");
DisposHandle(PortResH);
UpdateResFile(CurResFile());
ReportResError("SavePatch/UpdateResFile() (make sure disk is unlocked)");
// Get new configurateion.
PortResH = (Handle) MIDIGetPortInfo(arpClientID, PortID);
// Save new configurateion.
addresource(PortResH, portResType, PortInfoResID, PortInfoResName);
ReportResError("SavePatch/addresource()");
WriteResource(PortResH);
ReportResError("SavePatch/WriteResource() (make sure disk is unlocked)");
UpdateResFile(CurResFile());
ReportResError("SavePatch/UpdateResFile() (make sure disk is unlocked)");
ReleaseResource(PortResH);
ReportResError("SavePatch/ReleaseResource() (make sure disk is unlocked)");
InitCursor();
}
// Get String representation of MIDI Mgr Version Num.
// See Mac Tech Note #189 for details.
char *
StdMacVerNumToStr(long VerNum, char *VerStr)
{
char *RetVal;
char MajVer, MinVer, VerStage, VerRev, BugFixVer = 0;
if (VerNum == 0)
{
RetVal = NULL;
}
else
{
MajVer = (VerNum & 0xFF000000) >> 24;
MinVer = (VerNum & 0x00FF0000) >> 16;
VerStage = (VerNum & 0x0000FF00) >> 8;
VerRev = (VerNum & 0x000000FF) >> 0;
BugFixVer = MinVer & 0x0F;
switch (VerStage)
{
case 0x20:
VerStage = 'd';
break;
case 0x40:
VerStage = 'a';
break;
case 0x60:
VerStage = 'b';
break;
case 0x80:
VerStage = '';
break;
default:
VerStage = '?';
break;
}
if (BugFixVer == 0)
{
sprintf(VerStr,"%X.%X%c%X",
MajVer, MinVer>>4, VerStage, VerRev);
}
else
{
sprintf(VerStr,"%X.%X.%X%c%X",
MajVer, MinVer >> 4, MinVer & 0x0F, VerStage, VerRev);
}
RetVal = VerStr;
}
return(RetVal);
}
// Exit gracefully on error.
void
Terminate(void)
{
MIDISignOut(arpClientID);
StopDialog();
ExitToShell();
}
void
DoMenuCommand(long MenuResult)
{
short MenuID;
short MenuItem;
short ItemHit;
Str255 DAName;
short DARefNum;
WindowPtr Window;
Window = FrontWindow();
MenuID = HiWord(MenuResult); // use macros for efficiency to...
MenuItem = LoWord(MenuResult); // get menu item number and menu number
switch ( MenuID ) {
case appleMenu:
switch ( MenuItem )
{
case aboutItem: // bring up alert for About.
ItemHit = Alert(arpAboutAlertID, NULL);
break;
default: // all non-About items in this menu are DAs
GetItem(GetMHandle(appleMenu), MenuItem, DAName);
DARefNum = OpenDeskAcc(DAName);
break;
}
break;
case fileMenu:
switch ( MenuItem )
{
case closeItem:
if ( IsDAWindow(Window) )
{
CloseDeskAcc(((WindowPeek) Window)->windowKind);
}
else if ( IsAppWindow(Window) )
{
GDone = true;
}
break;
case quitItem:
GDone = true;
break;
default:
break;
}
break;
case editMenu:
SystemEdit(MenuItem-1);
break;
default:
break;
}
HiliteMenu(0); // Unhighlight what MenuSelect (or MenuKey) hilited
}
void
AdjustMenus(void)
{
WindowPtr Frontmost;
MenuHandle menu;
Frontmost = FrontWindow();
menu = GetMHandle(editMenu);
if ( IsDAWindow(Frontmost) )
{
EnableItem(menu, undoItem);
EnableItem(menu, cutItem);
EnableItem(menu, copyItem);
EnableItem(menu, pasteItem);
EnableItem(menu, clearItem);
}
else if ( IsAppWindow(Frontmost) )
{
DisableItem(menu, undoItem);
DisableItem(menu, cutItem);
DisableItem(menu, copyItem);
DisableItem(menu, pasteItem);
DisableItem(menu, clearItem);
}
}
// Save the current location of the main dialog
// back into our dialog resource.
void
StdSaveDLOGLocation(DialogPtr TheDialogPtr, short TheDialogID)
{
Handle MyDLOGResHndl;
Rect WhereItIs;
MyDLOGResHndl = GetResource('DLOG', TheDialogID);
if (MyDLOGResHndl != NULL) {
HNoPurge(MyDLOGResHndl);
SetPort(TheDialogPtr);
WhereItIs = TheDialogPtr->portRect;
LocalToGlobal( (Point *) &(WhereItIs.top) );
LocalToGlobal( (Point *) &(WhereItIs.bottom) );
( *( (DialogTHndl) MyDLOGResHndl ) )->boundsRect = WhereItIs;
ChangedResource(MyDLOGResHndl);
WriteResource(MyDLOGResHndl);
HPurge(MyDLOGResHndl);
}
else
{
ArpAlert("Error in StdSaveDLOGLocation(): MyDLOGResHndl = NULL!");
}
}
// If our main dialog was saved out of
// reach (on a bigger monitor), then move
// it back within reach.
void
StdAdjustDLOGLocation(DialogPtr TheDialogPtr)
{
Point TestPoint;
TestPoint.h = TheDialogPtr->portRect.left + 6; // 6 is enough to grab
TestPoint.v = TheDialogPtr->portRect.top + 6; // 6 is enough to grab
LocalToGlobal(&TestPoint);
if ( !PtInRgn( TestPoint, GetGrayRgn() ) ) /* Reposition */
{
MoveWindow( (WindowPtr) TheDialogPtr,
qd.screenBits.bounds.left + 50,
qd.screenBits.bounds.top + 50,
false
);
}
}
// Change the state of a radio button,
// and set their identifier vars accordingly.
void
SwitchRadio(DialogPtr TheDialog, short *CurRadio, short NextRadio)
{
ChangeState (TheDialog, 0, *CurRadio);
ChangeState (TheDialog, 1, NextRadio);
*CurRadio = NextRadio;
}
// Set the state of a binary control, such as a radio button or
// checkbox.
void
ChangeState(DialogPtr TheDialog, short State, short ItemNo)
{
short ItemType;
Handle ItemHndl;
Rect ItemBox;
GetDItem(TheDialog, ItemNo, &ItemType, &ItemHndl, &ItemBox);
SetCtlValue ( (ControlHandle) ItemHndl, (State ? 1 : 0) );
}
// Print out an Alert message to the user.
void
ArpAlert(char *TheMessage)
{
paramtext(TheMessage, "","","");
Alert(arpAlertBoxID, NULL);
}
// Convert a C String (from Cstr) into a Pascal string,
// copying the result into a buffer (Pstr).
char *
C2PStrCpy(char *Cstr, Str255 Pstr)
{
short i, Len = strlen(Cstr);
for(i=Len; i>0; i--) {
Pstr[i] = Cstr[i-1];
}
Pstr[i] = Len;
return( (char *) Pstr );
}
Boolean
IsAppWindow(WindowPtr window)
{
if ( window == nil )
{ return false;
}
else /* application windows have non-negative windowKinds */
{ return ((WindowPeek) window)->windowKind >= 0;
}
}
Boolean
IsDAWindow(WindowPtr Window)
{
if ( Window == nil )
{ return false;
}
else /* DA windows have negative windowKinds */
{ return ((WindowPeek) Window)->windowKind < 0;
}
}
// Boldface a button.
void
StdHiliteButton(DialogPtr TheDialog, short TheItemID)
{
short ItemType;
Handle ItemHndl;
Rect ItemBox;
GetDItem(TheDialog, TheItemID, &ItemType, &ItemHndl, &ItemBox);
PenSize(3,3);
InsetRect(&ItemBox,-4,-4);
FrameRoundRect(&ItemBox,16,16);
PenSize(1,1);
}
// Seed Random().
short
Seed(void)
{
unsigned long secs;
GetDateTime(&secs);
return( qd.randSeed = secs);
}
// Returns positive random result between 0 and max-1 inclusive.
short
Choose(short Max)
{
long RawResult = labs( (long) Random() );
return (short) ((RawResult*Max) / 32768);
}
// Alert user to Resource Manager Error.
void
ReportResError(char *Msg)
{
OSErr TheErr;
char Buf[256];
if ( (TheErr = ResError()) != noErr)
{
InitCursor();
sprintf(Buf,"ResError %d: %s...Aborting.", TheErr, Msg);
ArpAlert(Buf);
Terminate();
}
}